﻿using UnityEngine;
using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;

namespace Hont.AStar
{
    public class HontAStarUnityHelper
    {
        static HontAStarUnity mLambdaCache_MatchToWalkableNearsPoint_HontAStarUnity;
        static Vector3 mLambdaCache_MatchToWalkableNearestPoint_position;

        static GameObject mAstarDebugRoot;

        public static Vector3 MatchToWalkableNearestPoint(HontAStarUnity astar, Vector3 position)
        {
            if (astar.OctTree == null)
                throw new NotSupportedException("Not support Non-'OCT Tree' astar pathfinding mode!");

            mLambdaCache_MatchToWalkableNearsPoint_HontAStarUnity = astar;
            mLambdaCache_MatchToWalkableNearestPoint_position = position;

            var items = astar.OctTree.Root.GetItems(LambdaCache_MatchToWalkableNearestPoint_GetOctTreeItems);

            if (items == null || items.Count == 0)
                return position;

            items.Sort(LambdaCache_MatchToWalkableNearestPoint_Sort);

            return items[0].Position;
        }

        public static IEnumerator EasyPathMove(Transform seeker, Vector3[] pathArr, float speed = 30f, float error = 0.1f)
        {
            for (int i = 0; i < pathArr.Length; i++)
            {
                var point = pathArr[i];

                while (true)
                {
                    if (Vector3.Distance(seeker.transform.position, point) < error)
                        break;

                    seeker.transform.position = Vector3.MoveTowards(seeker.transform.position, point, speed * Time.deltaTime);
                    yield return new WaitForEndOfFrame();
                }
            }
        }

        public static void DebugPathfindingPath(Vector3[] pathArr, float duration = 1f)
        {
            var go = CreatePathfindingPathDebuger(pathArr).gameObject;

            UnityEngine.Object.Destroy(go, duration);
        }

        public static HontAStarUnityPathPointDebuger CreatePathfindingPathDebuger(Vector3[] pathArr)
        {
            if (!mAstarDebugRoot)
                mAstarDebugRoot = new GameObject("[AStar Debug]");

            var go = new GameObject("Pathfinding Debug Point" + Time.time + " __" + pathArr.Length);
            go.transform.SetParent(mAstarDebugRoot.transform);

            var pathPoint = go.AddComponent<HontAStarUnityPathPointDebuger>();
            pathPoint.pathArr = pathArr;

            return pathPoint;
        }

        static float LambdaCache_MatchToWalkableNearestPoint_GetOctTreeItems(OCTNode<Position> arg)
        {
            var result = -Vector3.Distance(arg.Bounds.center, mLambdaCache_MatchToWalkableNearestPoint_position);

            if ((bool)arg.UserDataDict[HontAStarUnity.OCTTREEUSERDATA_HAS_WALKABLE_BOX] == false)
                result = float.MinValue;

            return result;
        }

        static int LambdaCache_MatchToWalkableNearestPoint_Sort(IOCTItem<Position> x, IOCTItem<Position> y)
        {
            var astar = mLambdaCache_MatchToWalkableNearsPoint_HontAStarUnity;
            var position = mLambdaCache_MatchToWalkableNearestPoint_position;

            var a = (x.Position - position).sqrMagnitude.CompareTo((y.Position - position).sqrMagnitude);
            var b = astar.Grid.GetIsWalkable(x.Value).CompareTo(astar.Grid.GetIsWalkable(y.Value));
            return a + (-b) * 2;
        }
    }
}
